#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""Predesys Server - Core - Configuration management 
@version: 0.2.5
@license: GPL 3.0 (see "license" file)
@author: Jose A. Jimenez
@contact: jajimc@gmail.com"""

import sys
sys.dont_write_bytecode = True
import os
import os.path
DIR = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
sys.path.append(os.path.join(DIR, "../errorlog"))
import errorlog
os.chdir(DIR)
import exceptions
import security
import coreexceptions
import simplexml

# Configuration file path to load
CONFIG_FILE_1 = "configuration.xml"
CONFIG_FILE_2 = "/etc/predesys-server/configuration.xml"

# Default web service settings
DEFAULT_WEBSERVICE_HOST = "localhost"
DEFAULT_WEBSERVICE_PORT = 8000

class CoreConfiguration():
    """Class that represents the Predesys Server Core settings."""
    def __init__(self):
        """Class constructor. It initializes the object with the default values."""
        self.set_default_values()
        
    def set_default_values(self):
        """Sets the default values to all fields."""
        self.set_db_type(None)
        self.set_db_host(None)
        self.set_db_port(None)
        self.set_db_name(None)
        self.set_db_user_name(None)
        self.set_db_user_password(None)
        self.set_webservice_host(DEFAULT_WEBSERVICE_HOST)
        self.set_webservice_port(DEFAULT_WEBSERVICE_PORT)
        
    def get_db_type(self):
        return self.__dbType
        
    def get_db_host(self):
        return self.__dbHost
        
    def get_db_port(self):
        return self.__dbPort
        
    def get_db_name(self):
        return self.__dbName
    
    def get_db_user_name(self):
        return self.__dbUserName
        
    def get_db_user_password(self):
        return self.__dbUserPassword
        
    def get_webservice_host(self):
        return self.__webServiceHost
        
    def get_webservice_port(self):
        return self.__webServicePort
        
    def set_db_type(self, dbType):
        if dbType != None and type(dbType) != str: raise coreexceptions.ValueNotValidException("'dbType' must be null or a string.")
        self.__dbType = dbType
        
    def set_db_host(self, host):
        if host != None and type(host) != str: raise coreexceptions.ValueNotValidException("'host' must be null or a string.")
        self.__dbHost = host
        
    def set_db_port(self, port):
        if port != None and type(port) != int: raise coreexceptions.ValueNotValidException("'port' must be null or an integer.")
        self.__dbPort = port
        
    def set_db_name(self, name):
        if name != None and type(name) != str: raise coreexceptions.ValueNotValidException("'name' must be null or a string.")
        self.__dbName = name
        
    def set_db_user_name(self, name):
        if name != None and type(name) != str: raise coreexceptions.ValueNotValidException("'name' must be null or a string.")
        self.__dbUserName = name
        
    def set_db_user_password(self, password):
        if password != None and type(password) != str: raise coreexceptions.ValueNotValidException("'password' must be null or a string.")
        self.__dbUserPassword = password
        
    def set_webservice_host(self, host):
        if host == None or type(host) != str: raise coreexceptions.ValueNotValidException("Host must be null or a string.")
        self.__webServiceHost = host

    def set_webservice_port(self, port):
        if port == None or type(port) != int: raise coreexceptions.ValueNotValidException("Port must be null or an integer.")
        self.__webServicePort = port
        
    def load_from_file(self, path):
        """Loads settings from a XML file.
        @param path: File path from which load settings.
        @type path: C{str}
        @raise coreexceptions.ValueNotValidException: If path isn't a valid file path.
        @raise coreexceptions.FileNotFoundException: If file doesn't exist.
        @raise coreexceptions.LoadFileException: If an error occurs loading the file."""
        if path == None or type(path) != str or path == "": raise coreexceptions.ValueNotValidException("Path must be a valid file path.")
        if not (os.path.exists(path) and os.path.isfile(path)): raise coreexceptions.FileNotFoundException("Configuration file doesn't exist.")
        
        try:
            document = simplexml.SimpleXmlDocument(path)

            dbType = document.get_text_node_value("db_type", 0)
            if dbType == "": dbType = None
            self.set_db_type(dbType)
           
            dbHost = document.get_text_node_value("db_host", 0)
            self.set_db_host(dbHost)

            dbPort = document.get_text_node_value("db_port", 0)
            if dbPort == "": dbPort = None
            else: dbPort = int(dbPort)
            self.set_db_port(dbPort)
            
            dbName = document.get_text_node_value("db_name", 0)
            self.set_db_name(dbName)
            
            dbUserName = document.get_text_node_value("db_user_name", 0)
            self.set_db_user_name(dbUserName)
            
            dbUserPassword = document.get_text_node_value("db_user_password", 0)
            self.set_db_user_password(dbUserPassword)
            
            webServiceHost = document.get_text_node_value("web_service_host", 0)
            self.set_webservice_host(webServiceHost)
            
            webServicePort = document.get_text_node_value("web_service_port", 0)
            if webServicePort == "": webServicePort = None
            else: webServicePort = int(webServicePort)
            self.set_webservice_port(webServicePort)
            
        except:
            raise coreexceptions.LoadFileException("Configuration load error.")
        
    def save_to_file(self, path):
        """Saves settings to a file.
        @param path: File path to save settings.
        @type path: C{str}
        @raise coreexceptions.ValueNotValidException: If path isn't a valid file path.
        @raise coreexceptions.SaveFileException: If an error occurs saving the file."""
        if path == None or type(path) != str or path == "": raise coreexceptions.ValueNotValidException("Path must be a valid file path.")
        
        content = "<?xml version=\"1.0\" ?>\n"
        content += "<configuration>\n"
        
        content += "\t<db_type>%s</db_type>\n" % self.get_db_type()
        content += "\t<db_host>%s</db_host>\n" % self.get_db_host()
        
        dbPort = self.get_db_port()
        if dbPort != None: content += "\t<db_port>%d</db_port>\n" % dbPort
        else: content += "\t<db_port></db_port>\n"
        
        content += "\t<db_name>%s</db_name>\n" % self.get_db_name()
        content += "\t<db_user_name>%s</db_user_name>\n" % self.get_db_user_name()
        content += "\t<db_user_password>%s</db_user_password>\n" % self.get_db_user_password()
        
        content += "\t<web_service_host>%s</web_service_host>\n" % self.get_webservice_host()
        content += "\t<web_service_port>%d</web_service_port>\n" % self.get_webservice_port()
        
        content += "</configuration>"
        
        try:
            with open(path, "w") as configFile:
                configFile.write(content)
        except:
            raise coreexceptions.SaveFileException("Configuration save error.")
    
def load_configuration():
    """Loads configuration from CONFIG_FILE_1 file or CONFIG_FILE_2 file. If an error occurs, it sets the default configuration.
    @return: Loaded configuration
    @rtype: L{CoreConfiguration}"""
    config = CoreConfiguration()
    
    if os.path.isfile(CONFIG_FILE_1):
        try:
            config.load_from_file(CONFIG_FILE_1)
        except Exception as exc1:
            if os.path.isfile(CONFIG_FILE_2):
                try:
                    config.load_from_file(CONFIG_FILE_2)
                except Exception as exc2:
                    errorlog.log_exception(exc2.args[0], "predesys-server.core.configuration.load_configuration")
                    config.set_default_values()
            else:
                errorlog.log_exception(exc1.args[0], "predesys-server.core.configuration.load_configuration")
                config.set_default_values()
    elif os.path.isfile(CONFIG_FILE_2):
        try:
            config.load_from_file(CONFIG_FILE_2)
        except Exception as exc3:
            errorlog.log_exception(exc3.args[0], "predesys-server.core.configuration.load_configuration")
            config.set_default_values()
    else:
        config.set_default_values()

    return config

def save_configuration(config):
    """Saves a given configuration to CONFIG_FILE_1 file or CONFIG_FILE_2 file.
    @param config: Configuration to save
    @type config: L{CoreConfiguration}
    @raise coreexceptions.SaveFileException: If an error occurs saving the configuration."""
    security.check_root()
    if config == None or not isinstance(config, CoreConfiguration): raise coreexceptions.ValueNotValidException("'config' must be a not null CoreConfiguration object.")
    
    if os.path.isfile(CONFIG_FILE_1):
        try:
            config.save_to_file(CONFIG_FILE_1)
        except Exception as exc1:
            if os.path.isfile(CONFIG_FILE_2):
                try:
                    config.save_to_file(CONFIG_FILE_2)
                except Exception as exc2:
                    errorlog.log_exception(exc2.args[0], "predesys-server.core.configuration.save_configuration")
                    raise coreexceptions.SaveFileException("Saving configuration error")
            else:
                errorlog.log_exception(exc1.args[0], "predesys-server.core.configuration.save_configuration")
                raise coreexceptions.SaveFileException("Saving configuration error")
    elif os.path.isfile(CONFIG_FILE_2):
        try:
            config.save_to_file(CONFIG_FILE_2)
        except Exception as exc3:
            errorlog.log_exception(exc3.args[0], "predesys-server.core.configuration.save_configuration")
            raise coreexceptions.SaveFileException("Saving configuration error")
    else:
        errorlog.log_exception(Exception("No configuration file."), "predesys-server.core.configuration.save_configuration")
        raise coreexceptions.SaveFileException("Saving configuration error")
